package me.zhengjie.modules.fund.service.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.exception.BadRequestException;
import me.zhengjie.modules.fund.domain.Fund;
import me.zhengjie.modules.fund.domain.FundNetValue;
import me.zhengjie.modules.fund.domain.UserFund;
import me.zhengjie.modules.fund.domain.UserFundLog;
import me.zhengjie.modules.fund.repository.UserFundRepository;
import me.zhengjie.modules.fund.service.*;
import me.zhengjie.modules.fund.service.dto.*;
import me.zhengjie.modules.fund.service.mapstruct.UserFundMapper;
import me.zhengjie.utils.ElAdminConstant;
import me.zhengjie.utils.QueryHelp;
import me.zhengjie.utils.ValidationUtil;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.ParseException;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class UserFundServiceImpl implements UserFundService {

    @Resource
    private UserFundRepository userFundRepository;

    @Resource
    private UserFundMapper userFundMapper;

    @Resource
    private FundService fundService;

    @Resource
    private UserFundFlowService userFundFlowService;

    @Resource
    private FundNetValueService fundNetValueService;

    @Resource
    private UserFundLogService userFundLogService;

    /**
     * @param resources /
     * @see UserFundService#create(UserFund)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(UserFund resources) {
        Fund fund = fundService.findByCode(resources.getFundCode());
        if (Objects.isNull(fund)) {
            throw new BadRequestException("基金编码：" + resources.getFundCode() + " 不存在");
        }
        resources.setFundName(fund.getName());

        UserFund exists = userFundRepository.findByUserIdAndFundCode(resources.getUserId(), resources.getFundCode());
        if (Objects.nonNull(exists)) {
            throw new BadRequestException("此用户已录入此基金，请不要重复录入");
        }

        resources.setPrincipalAmount(resources.getPrincipalNetValue().multiply(resources.getCount()));

        try {
            FundNetValue newestFundNetValue = fundNetValueService.findNewestByCode(resources.getFundCode());
            if (Objects.nonNull(newestFundNetValue)) {
                resources.setNewestNetValue(newestFundNetValue.getNetValue());
                resources.setNewestAmount(resources.getNewestNetValue().multiply(resources.getCount()));
                resources.setNewestUpdatedDate(newestFundNetValue.getDate());
            }
        } catch (ParseException e) {
            log.error("获取最新基金失败", e);
        }

        // 初始化日志记入买入日志吧

//        UserFundFlow userFundFlow = new UserFundFlow();
//        userFundFlow.setUserId(resources.getUserId());
//        userFundFlow.setFundCode(resources.getFundCode());
//        userFundFlow.setFundName(resources.getFundName());
//        userFundFlow.setDirection(ElAdminConstant.UserFundDirection.BUY);
//        userFundFlow.setOperateAmount(resources.getPrincipalAmount());
//        userFundFlow.setOperateCount(resources.getCount());
//        userFundFlow.setConfirmNetValue(resources.getPrincipalNetValue());
//        userFundFlow.setOperateDate(DateUtil.parse(DateUtil.today(), DatePattern.NORM_DATE_FORMAT));
//        userFundFlow.setServiceFee(BigDecimal.ZERO);
//        userFundFlowService.create(userFundFlow);

        userFundRepository.save(resources);
    }

    /**
     * @param resources
     * @see UserFundService#update(UserFund)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(UserFund resources) {
        UserFund userFund = userFundRepository.findById(resources.getId()).orElseGet(UserFund::new);
        ValidationUtil.isNull(userFund.getId(), "UserFund", "id", resources.getId());
        resources.setId(userFund.getId());

        Fund fund = fundService.findByCode(resources.getFundCode());
        if (Objects.isNull(fund)) {
            throw new BadRequestException("基金编码：" + resources.getFundCode() + "不存在");
        }
        resources.setFundName(fund.getName());
        resources.setPrincipalAmount(resources.getPrincipalNetValue().multiply(resources.getCount()));
        resources.setNewestAmount(userFund.getNewestNetValue().multiply(resources.getCount()));

        userFundRepository.save(resources);
    }

    /**
     * @param resources
     * @see UserFundService#updateNetValue(FundNetValue)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateNetValue(FundNetValue resources) {
        List<UserFund> userFunds = userFundRepository.findByFundCode(resources.getCode());
        if (CollectionUtils.isEmpty(userFunds)) {
            return;
        }

        for (UserFund userFund : userFunds) {
            userFund.setNewestNetValue(resources.getNetValue());
            userFund.setNewestAmount(userFund.getCount().multiply(userFund.getNewestNetValue()));
            userFund.setNewestUpdatedDate(resources.getDate());
        }

        userFundRepository.saveAll(userFunds);
    }

    /**
     * @param ids 要删除的ids
     * @see UserFundService#delete(Set)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Set<Long> ids) {
        userFundRepository.deleteAllByIdIn(ids);
    }

    /**
     * @param criteria 查询条件
     * @return
     * @see UserFundService#queryAll(UserFundQueryCriteria, Pageable)
     */
    @Override
    public Page<UserFundDto> queryAll(UserFundQueryCriteria criteria, Pageable pageable) {
        Page<UserFund> pageUserFunds = userFundRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root, criteria, criteriaBuilder), pageable);
        List<UserFundDto> userFundDtos = pageUserFunds.map(userFundMapper::toDto).getContent();

        BigDecimal totalPrincipleAmount = BigDecimal.ZERO; // 总本金
        BigDecimal totalNewestAmount = BigDecimal.ZERO; // 最新金额
        BigDecimal totalDealAmountOfCurrentMonth = BigDecimal.ZERO; // 当月交易金额

        BigDecimal totalPrincipleAmountOfYesterday = BigDecimal.ZERO; // 昨日本金
        BigDecimal totalProfitAmountOfYesterday = BigDecimal.ZERO; // 昨日盈利

        DateTime yesterday = me.zhengjie.utils.DateUtil.getYesterdayDate();
        DateTime beforeYesterday = me.zhengjie.utils.DateUtil.getTheDayBeforeYesterdayDate();
        List<UserFundLog> userFundLogs = queryLogs(criteria.getUserId(), beforeYesterday, yesterday);

        List<UserFundFlowDto> flowsOfCurrentMonth = queryCurrentMonthFlows(criteria); // 操作日志
        for (UserFundDto userFundDto : userFundDtos) {
            userFundDto.setProfitAmount(userFundDto.getNewestAmount().subtract(userFundDto.getPrincipalAmount()));
            userFundDto.setProfitRate(userFundDto.getProfitAmount().divide(userFundDto.getPrincipalAmount(), 4, BigDecimal.ROUND_HALF_UP).doubleValue());
            flowsOfCurrentMonth.stream()
                    .filter(f -> f.getFundCode().equals(userFundDto.getFundCode()))
                    .forEach((flow) -> {
                        userFundDto.setDealAmountOfCurrentMonth(userFundDto.getDealAmountOfCurrentMonth().add(flow.getOperateAmount()));
                        if (flow.getStatus() == 2) {
                            userFundDto.setDealAmountOfCurrentMonthOnOrder(userFundDto.getDealAmountOfCurrentMonthOnOrder().add(flow.getOperateAmount()));
                        }
                    });
            // 前日日志
//            UserFundLog beforeYesterdayLog = userFundLogs.stream()
//                    .filter(f -> f.getFundCode().equals(userFundDto.getFundCode()) && beforeYesterday.equals(f.getDate()))
//                    .findFirst().orElse(null);

            // 昨日日志 获取昨日盈亏
            UserFundLog yesterdayLog = userFundLogs.stream()
                    .filter(f -> f.getFundCode().equals(userFundDto.getFundCode()) && yesterday.equals(f.getDate()))
                    .findFirst().orElse(null);
            if (Objects.nonNull(yesterdayLog)) {
                userFundDto.setProfitAmountOfYesterday(yesterdayLog.getProfitAmount());
                userFundDto.setProfitRateOfYesterday(yesterdayLog.getProfitRate().doubleValue());

                totalPrincipleAmountOfYesterday = totalPrincipleAmountOfYesterday.add(yesterdayLog.getPrincipalAmount());
                totalProfitAmountOfYesterday = totalProfitAmountOfYesterday.add(yesterdayLog.getProfitAmount());
            } else {
                userFundDto.setProfitAmountOfYesterday(BigDecimal.ZERO);
                userFundDto.setProfitRateOfYesterday(0d);
            }
//
//            // 昨日盈利 = 昨日金额（昨日金额+在途） - 前日金额（前日金额+在途）
//            if (Objects.nonNull(beforeYesterdayLog) && Objects.nonNull(yesterdayLog)) {
//                BigDecimal onOrderAmountOfYesterday = Objects.isNull(yesterdayLog.getOnorderAmount()) ? BigDecimal.ZERO : yesterdayLog.getOnorderAmount();
//                BigDecimal onOrderAmountOfBeforeYesterday = Objects.isNull(beforeYesterdayLog.getOnorderAmount()) ? BigDecimal.ZERO : beforeYesterdayLog.getOnorderAmount();
//
//                userFundDto.setProfitAmountOfYesterday(yesterdayLog.getNewestAmount().add(onOrderAmountOfYesterday).subtract(beforeYesterdayLog.getNewestAmount()).subtract(onOrderAmountOfBeforeYesterday));
//                userFundDto.setProfitRateOfYesterday(userFundDto.getProfitAmountOfYesterday().divide((beforeYesterdayLog.getNewestAmount().add(onOrderAmountOfBeforeYesterday)), 4, BigDecimal.ROUND_HALF_UP).doubleValue());
//
//                totalProfitAmountOfYesterday = totalProfitAmountOfYesterday.add(userFundDto.getProfitAmountOfYesterday());
//
//                totalPrincipleAmountOfYesterday = totalPrincipleAmountOfYesterday.add(beforeYesterdayLog.getNewestAmount()).add(onOrderAmountOfBeforeYesterday);
//            } else {
//                userFundDto.setProfitRateOfYesterday(0d);
//            }

            totalPrincipleAmount = totalPrincipleAmount.add(userFundDto.getPrincipalAmount());
            totalNewestAmount = totalNewestAmount.add(userFundDto.getNewestAmount());
            totalDealAmountOfCurrentMonth = totalDealAmountOfCurrentMonth.add(userFundDto.getDealAmountOfCurrentMonth());
        }

        // 排序
        userFundDtos = sorted(criteria, userFundDtos);

        List<UserFundDto> result = new ArrayList<>();

        // 如果有数据，添加一条汇总信息在第一条
        if (!CollectionUtils.isEmpty(userFundDtos)) {
            UserFundDto totalUserFund = new UserFundDto();
            totalUserFund.setFundName("合计");
            totalUserFund.setPrincipalAmount(totalPrincipleAmount);
            totalUserFund.setNewestAmount(totalNewestAmount);
            totalUserFund.setProfitAmount(totalNewestAmount.subtract(totalPrincipleAmount));
            totalUserFund.setProfitRate(totalUserFund.getProfitAmount().divide(totalPrincipleAmount, 4, BigDecimal.ROUND_HALF_UP).doubleValue());
            totalUserFund.setDealAmountOfCurrentMonth(totalDealAmountOfCurrentMonth);
            totalUserFund.setProfitAmountOfYesterday(totalProfitAmountOfYesterday);
            if (BigDecimal.ZERO.compareTo(totalPrincipleAmountOfYesterday) != 0) {
                totalUserFund.setProfitRateOfYesterday(totalProfitAmountOfYesterday.divide(totalPrincipleAmountOfYesterday, 4, BigDecimal.ROUND_HALF_UP).doubleValue());
            }
            result.add(totalUserFund);
        }

        result.addAll(userFundDtos);

        PageImpl<UserFundDto> pageUserFundDtos = new PageImpl<>(result, pageable, pageUserFunds.getTotalElements());
        return pageUserFundDtos;
    }

    /**
     * 根据条件进行排序
     *
     * @param criteria
     * @param userFundDtos
     * @return
     */
    private List<UserFundDto> sorted(UserFundQueryCriteria criteria, List<UserFundDto> userFundDtos) {
        if (Objects.nonNull(criteria.getSortBy()) && Objects.nonNull(criteria.getSortDirection())) {
            switch (criteria.getSortBy()) {
                case ElAdminConstant.UserFundSortBy.PROFIT_RATE:
                    if ("asc".equals(criteria.getSortDirection())) {
                        userFundDtos = userFundDtos.stream().sorted(Comparator.comparing(UserFundDto::getProfitRate)).collect(Collectors.toList());
                    } else {
                        userFundDtos = userFundDtos.stream().sorted(Comparator.comparing(UserFundDto::getProfitRate).reversed()).collect(Collectors.toList());
                    }
                    break;
                case ElAdminConstant.UserFundSortBy.PROFIT_RATE_YESTERDAY:
                    if ("asc".equals(criteria.getSortDirection())) {
                        userFundDtos = userFundDtos.stream().sorted(Comparator.comparing(UserFundDto::getProfitRateOfYesterday)).collect(Collectors.toList());
                    } else {
                        userFundDtos = userFundDtos.stream().sorted(Comparator.comparing(UserFundDto::getProfitRateOfYesterday).reversed()).collect(Collectors.toList());
                    }
                    break;
                case ElAdminConstant.UserFundSortBy.PROFIT_AMOUNT_YESTERDAY:
                    if ("asc".equals(criteria.getSortDirection())) {
                        userFundDtos = userFundDtos.stream().sorted(Comparator.comparing(UserFundDto::getProfitAmountOfYesterday)).collect(Collectors.toList());
                    } else {
                        userFundDtos = userFundDtos.stream().sorted(Comparator.comparing(UserFundDto::getProfitAmountOfYesterday).reversed()).collect(Collectors.toList());
                    }
                    break;
            }
        }
        return userFundDtos;
    }

    /**
     * 获取当月的所有操作纪录流水
     *
     * @param criteria
     * @return
     */
    private List<UserFundFlowDto> queryCurrentMonthFlows(UserFundQueryCriteria criteria) {
        UserFundFlowQueryCriteria query = new UserFundFlowQueryCriteria();
        query.setUserId(criteria.getUserId());

        List<Timestamp> operateDates = new ArrayList<>(2);
        operateDates.add(DateUtil.beginOfMonth(new Date()).toTimestamp());
        operateDates.add(DateUtil.endOfMonth(new Date()).toTimestamp());
        query.setOperateDate(operateDates);

        List<UserFundFlowDto> userFundFlowDtos = userFundFlowService.queryAll(query);
        return userFundFlowDtos;
    }

    private List<UserFundLog> queryLogs(Long userId, DateTime start, DateTime end) {
        List<Timestamp> dates = new ArrayList<>(2);
        dates.add(start.toTimestamp());
        dates.add(end.toTimestamp());

        UserFundLogQueryCriteria query = new UserFundLogQueryCriteria();
        query.setUserId(3l);
        query.setDate(dates);

        return userFundLogService.queryAll(query);
    }
}
